package com.itfreer.file.remote.upload;

import com.itfreer.file.FileSegmentInfo;
import com.itfreer.file.remote.FileProgressListener;
import com.itfreer.file.remote.FileService;
import com.itfreer.utils.collection.ArrayDeque;
import com.itfreer.utils.collection.Queue;
import com.itfreer.utils.common.PathUtils;

import java.io.File;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

/**
 * 定义文件上传器
 */
public class FileUploader {

    // 需要下载的文件
    private String bucketName;
    // 需要下载的文件
    private String objectKey;
    // 文件块大小，10kb
    private Integer segmentSize = 102400;
    // 文件下载队列
    private final Queue<Long> downQueue;
    // 当前的进程数
    private final List<Long> threadList = new ArrayList<>();
    private int threads = 3;
    private Long segmentCount;

    public FileUploader(String bucketName, String objectKey){
        this.bucketName = bucketName;
        this.objectKey = objectKey;
        this.downQueue = new ArrayDeque<Long>();
    }

    /**
     * 开始文件下载
     */
    public void uploade(final FileProgressListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                first(listener);
            }
        }).start();
    }

    /**
     * 开始文件下载
     */
    public void uploade() {
        first();
    }

    /**
     * 第一块下载
     */
    private void first(){
        // 获取文件大小，计算文件块
        String path = PathUtils.GetSystemPath() + "file/";
        if (path.endsWith("/")) {
            path += bucketName + "/";
        } else {
            path += "/" + bucketName + "/";
        }
        String filePath = path + objectKey;
        File fileInfo = new File(filePath);
        if (!fileInfo.exists()) {
            return;
        }

        // 计算文件块总数
        segmentCount = (fileInfo.length() / this.segmentSize) + (fileInfo.length() % this.segmentSize > 0 ? 1 : 0);
        // 将后续文件块存储到队列中
        for(long i=0;i<segmentCount;i++){
            downQueue.add(i);
        }

        // 开始上传
        Long black = downQueue.poll();
        while (black!=null){
            FileSegmentInfo segmentInfo = getBlack(fileInfo, black);
            Boolean fInfo = FileService.instance.uploadFile(bucketName, objectKey, segmentInfo);
            if(fInfo==null || fInfo == false){
                // 失败，则退回到队列重新下载
                downQueue.offer(segmentInfo.getSegmentIndex());
            }
            black = downQueue.poll();
        }
    }

    /**
     * 第一块下载
     */
    private void first(final FileProgressListener listener){
        // 获取文件大小，计算文件块
        String path = PathUtils.GetSystemPath() + "file/";
        if (path.endsWith("/")) {
            path += bucketName + "/";
        } else {
            path += "/" + bucketName + "/";
        }
        String filePath = path + objectKey;
        File fileInfo = new File(filePath);
        if (!fileInfo.exists()) {
            return;
        }

        // 计算文件块总数
        segmentCount = (fileInfo.length() / this.segmentSize) + (fileInfo.length() % this.segmentSize > 0 ? 1 : 0);
        // 将后续文件块存储到队列中
        for(long i=0;i<=segmentCount;i++){
            downQueue.add(i);
        }

        // 开始上传
        Long black = downQueue.poll();
        while (black!=null){
            if(threadList.size() >= threads){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                FileSegmentInfo segmentInfo = getBlack(fileInfo, black);
                UploadThread uploadThread = new UploadThread(bucketName, objectKey,
                        segmentInfo, downQueue, threadList, listener);
                uploadThread.setPriority(7);
                uploadThread.start();
                threadList.add(uploadThread.getId());
                black = downQueue.poll();
            }
        }
    }

    private FileSegmentInfo getBlack(File fileInfo, Long index){
        FileSegmentInfo fileSegmentInfo = new FileSegmentInfo();
        fileSegmentInfo.setSegmentIndex(index);
        fileSegmentInfo.setSegmentSize(segmentSize);
        fileSegmentInfo.setLastSegment(index == segmentCount - 1);

        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile(fileInfo, "r");
            randomAccessFile.seek(fileSegmentInfo.getSegmentIndex() * fileSegmentInfo.getSegmentSize());
            byte[] buffer = null;

            if (fileSegmentInfo.isLastSegment()) {
                int lastLength = (int) (randomAccessFile.length()
                        - fileSegmentInfo.getSegmentIndex() * fileSegmentInfo.getSegmentSize());
                buffer = new byte[lastLength];
                randomAccessFile.read(buffer, 0, lastLength);
                fileSegmentInfo.setSegmentContent(buffer);
            } else {
                buffer = new byte[fileSegmentInfo.getSegmentSize()];
                randomAccessFile.read(buffer, 0, fileSegmentInfo.getSegmentSize());
                fileSegmentInfo.setSegmentContent(buffer);
            }
            randomAccessFile.close();
            return fileSegmentInfo;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}